import numpy as np
import random

# Jeu de données : 5 documents, 3 termes (T1, T2, T3)
documents = [
    np.array([1, 3, 3]),   # d1
    np.array([2, 1, 0]),   # d2
    np.array([0, 1, 0]),   # d3
    np.array([1, 3, 1]),   # d4
    np.array([1, 0, 1]),   # d5
]


# Jeu de données étendu : 20 documents, 5 termes
# Groupe 1 (thématique 'sciences')  : termes T1, T2 prédominants
# Groupe 2 (thématique 'histoire')  : termes T3, T4 prédominants
# Groupe 3 (thématique 'sport')     : terme T5 prédominant
random.seed(42)
def doc_groupe(termes_forts, termes_faibles, p=5):
    v = np.zeros(p, dtype=int)
    for t in termes_forts:
        v[t] = random.randint(3, 7)
    for t in termes_faibles:
        v[t] = random.randint(0, 2)
    return v

documents_grands = (
    [doc_groupe([0,1],[2,3,4]) for _ in range(3)]   +  # sciences
    [doc_groupe([2,3],[0,1,4]) for _ in range(4)]   +  # histoire
    [doc_groupe([4],  [0,1,2,3]) for _ in range(6)] +  # sport
    [doc_groupe([2,3],[0,1,4]) for _ in range(4)]   +  # histoire
    [doc_groupe([0,1],[2,3,4]) for _ in range(3)]      # sciences
 )


############################
# ALGORITHME DES K MOYENNES
############################

# Question 3 : fonction delta(di,dj)
def delta(di: np.ndarray, dj: np.ndarray) -> float:
    """
    Calcule la distance de Manhattan entre les vecteurs di et dj.
    Paramètres : di, dj — tableaux NumPy de même taille
    Retour     : float — somme des valeurs absolues des différences
    """
    # À compléter
    pass

# Tests de la question 3
print(delta(documents[0], documents[1]))  # Attendu : 6.0
print(delta(documents[1], documents[2]))  # Attendu : 2.0

# Question 4 : Fonction affectation
def affectation(documents: list, centres: list) -> list:
    """
    Retourne pour chaque document l'indice du centre le plus proche.
    Paramètres : documents — liste de vecteurs NumPy
                 centres  — liste de vecteurs NumPy (centres de classe)
    Retour     : list[int] — indices_centres_doc[i] = indice du centre le plus proche de documents[i]
    """
    # À compléter
    pass

# Tests de la question 4
c1 = documents[1]
c2 = documents[4]
print(affectation(documents,[c1,c2]))

# Question 5 : recalcul des centres
def recalcul_centres(documents: list, indices_centres: list, k: int) -> list:
    """
    Calcule les nouveaux centres de classe.
    Paramètres : documents       — liste de vecteurs NumPy
                 indices_centres — liste d'entiers (résultat de affectation)
                 k               — nombre de classes
    Retour     : list[np.ndarray] — nouveaux centres
    """
    # À compléter
    pass

# Tests de la question 5
print(recalcul_centres(documents,[1, 0, 0, 1, 1],2))

# Question 6 : Algorithme des K moyennes
def k_moyennes(documents: list, k: int, centres_init: list) -> tuple:
    """
    Algorithme des k-moyennes.
    Paramètres : documents    — liste de vecteurs NumPy
                 k            — nombre de classes
                 centres_init — liste de k vecteurs NumPy (centres initiaux)
    Retour     : (labels, centres) — labels : list[int], centres : list[np.ndarray]
    """
    # À compléter
    return (None,None)

# Vérification question 6
labels, centres = k_moyennes(documents, k=2,
                             centres_init=[documents[1].copy(), documents[4].copy()])
print('\nLabels finaux :', labels)
print('Centres finaux :', centres)

# Question 7
# Centres initiaux pour k-moyennes (un par groupe)
centres_init = [
    documents_grands[0],   # groupe sciences
    documents_grands[4],   # groupe histoire
    documents_grands[8],   # groupe sport
]
# k-moyennes
labels_km, centres_km = k_moyennes(documents_grands, 3, centres_init)
print('\nLabels finaux doc_grands:', labels_km)
print('Centres finaux doc_grands:', centres_km)

############################
# ALGORITHME SINGLE-PASS
############################


# Question 10 : Fonction dist_max
def dist_max(di: np.ndarray, centres: list, theta: float) -> bool:
    """
    Teste si di est plus loin que theta de TOUS les centres existants.
    Paramètres : di      — vecteur NumPy du document courant
                 centres — liste des centres de classe courants
                 theta   — seuil de distance
    Retour     : bool — True si di doit créer une nouvelle classe
    """
    # À compléter
    pass

# Tests
print("\n")
c_init = [documents[1]]
print(dist_max(documents[0], c_init, 5.0))  # Attendu : True  (delta(d1,c2)=6)
print(dist_max(documents[2], c_init, 5.0))  # Attendu : False (delta(d3,c2)=2)

# Question 11 : Fonction dist_min
def dist_min(di: np.ndarray, centres: list) -> int:
    """
    Retourne l'indice du centre le plus proche de di.
    Paramètres : di      — vecteur NumPy
                 centres — liste de vecteurs NumPy
    Retour     : int — indice ℓ tel que δ(di, centres[ℓ]) est minimal
    """
    # À compléter
    pass

# Tests
print("\n")
print(dist_min(documents[0],documents))
print(dist_min(documents[1],documents))
print(dist_min(documents[4],documents))

# Question 11 : Fonction recalcul_centre_sp
def recalcul_centre_sp(centres: list, l: int, classe_l: list) -> list:
    """
    Recalcule le centre cₗ après ajout d'un document à Aₗ.
    Paramètres : centres  — liste des centres courants (modifiée en place)
                 l        — indice de la classe à mettre à jour
                 classe_l — liste des vecteurs NumPy de la classe Aₗ
    Retour     : list — centres mis à jour
    """
    # À compléter
    pass

# Tests
print("\n")
centres = [documents[0], documents[1]]
classes = [[documents[0]], [documents[1],documents[2]]]
print(recalcul_centre_sp(centres,1,classes[1]))

# Question 13 : Algorithme Single-Pass
def single_pass(documents: list, theta: float) -> tuple:
    """
    Algorithme Single Pass.
    Paramètres : documents — liste de vecteurs NumPy (ordre d'arrivée important)
                 theta     — seuil de distance pour créer une nouvelle classe
    Retour     : (classes, centres)
                   classes : list[list[np.ndarray]] — classes[k] = liste des docs de Aₖ₊₁
                   centres : list[np.ndarray]       — centres[k] = centre de Aₖ₊₁
    """
    # Initialisation
    # À compléter
    return None, None


# Tests
print("\nTest de l'algorithme Single-pass")
centres, classes = single_pass(documents,5.0)
print("Centres :",centres)
print("Classes: ",classes)
















